package com.lumlord.common.shiro.util;

import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.lumlord.common.cache.CacheCenter;
import com.lumlord.common.domain.User;
import com.lumlord.common.exception.BusinessException;
import com.lumlord.service.LumlordCacheServer;
import com.lumlord.service.SystemUserService;
import com.lumlord.util.AccessAddressUtil;
import com.lumlord.util.DateUtils;
import com.lumlord.util.RedisUtil;

import com.lumlord.util.RequestToBean;
import org.apache.shiro.crypto.SecureRandomNumberGenerator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.io.UnsupportedEncodingException;
import java.util.Calendar;
import java.util.Date;

@Component
public class JwtUtils {


	public void setTokenRefresh(String token, String username,Integer userId) {
		// 刷新时间
		int expire = expirationHour*60*60; // 转小时计量
		String key = CacheCenter.$(username, User.class)+loginInfoCache;
		redisUtil.hset(key, "tokenValidTime", DateUtils.getAddDayHours(0), expire);
		redisUtil.hset(key, "expirationTime", DateUtils.getAddDayHours(expirationHour), expire);
		redisUtil.hset(key, "username", username, expire);
		redisUtil.hset(key, "ip", accessAddressUtil.getIpAddress(RequestToBean.getRequest()), expire);
		redisUtil.hset(key, "id", userId+"", expire);
		redisUtil.hset(key, "token", token, expire);
	}

	/**
	 * 登录成功提示
	 * @param username
	 * @throws BusinessException
	 */
	public void allowLoginTimes(String username)throws  BusinessException{
		int expire = unitMinuteLogin*60; // 转小时计量
		String key = CacheCenter.$(username, User.class)+allowLoginTimes;
		String lockTimesCacheKey = CacheCenter.$(username, User.class)+lockTimes;
		//当登录失败已拉入 黑名单。 不允许 通过
		if(redisUtil.hasKey(lockTimesCacheKey)){
			throw new BusinessException("账号登录失败已超过"+allowFailLogin+"次，账号已被锁定，稍后"+lockWatitTime+"分钟再试");
		}

		//登录 次数计时器
		if(null == redisUtil.get(key)){
			redisUtil.set(key,"1",expire);
		}else{
			Integer times = Integer.parseInt(redisUtil.get(key)+"")+1;
			if(times>allowSuccessLogin){
				throw new BusinessException("该账户该账户"+unitMinuteLogin+"分钟内频繁登录超过"+allowSuccessLogin+"次,账号已被锁定，稍后再试");
			}
			Long timeOut = redisUtil.getTimeOut(key);
			redisUtil.set(key,times+"",timeOut.intValue());
		}
	}
	/**
	 * 登录失败提示
	 * @param username
	 * @throws BusinessException
	 */
	public void loginFailCache(String username) throws BusinessException{
		int expire = unitMinute*60; // 转小时计量
		String key = CacheCenter.$(username, User.class)+loginFailTimes;
		String lockTimesCacheKey = CacheCenter.$(username, User.class)+lockTimes;
		if(null == redisUtil.get(key)){
			redisUtil.set(key,"1",expire);
		}else{
			Integer times = Integer.parseInt(redisUtil.get(key)+"")+1;
			if(times>allowFailLogin||redisUtil.hasKey(lockTimesCacheKey)){

				 if(!redisUtil.hasKey(lockTimesCacheKey)){
				 	redisUtil.set(lockTimesCacheKey,username+"锁定"+lockWatitTime+"分钟",lockWatitTime*60);
				 }
				throw new BusinessException("账号登录失败已超过"+allowFailLogin+"次，账号已被锁定，稍后"+lockWatitTime+"分钟再试");
			}
			Long timeOut = redisUtil.getTimeOut(key);
			redisUtil.set(key,times+"",timeOut.intValue());
		}
		systemUserService.removeCacheUser(username);
	}


	/**
	 * 获得token中的信息无需secret解密也能获得
	 * 
	 * @return token中包含的签发时间
	 */
	public  Date getIssuedAt(String token) {
		try {
			DecodedJWT jwt = JWT.decode(token);
			return jwt.getIssuedAt();
		} catch (JWTDecodeException e) {
			return null;
		}
	}

	/**
	 * 获得token中的信息超時日期
	 *
	 * @return token中包含的签发时间
	 */
	public  Date getExpiresAt(String token) {
		try {
			DecodedJWT jwt = JWT.decode(token);
			return jwt.getExpiresAt();
		} catch (JWTDecodeException e) {
			return null;
		}
	}

	/**
	 * 获得token中的信息无需secret解密也能获得
	 * 
	 * @return token中包含的用户名
	 */
	public  String getAccount(String token) {
		try {
			DecodedJWT jwt = JWT.decode(token);
			return jwt.getClaim(ACCOUNT).asString();
		} catch (JWTDecodeException e) {
			return null;
		}
	}


	/**
	 * 生成jwt token，设置过期时间为1小时
	 * 
	 * @param username 用户名
	 */
	public  String sign(String username) {

		long time = expirationHour*60*60*1000;

		try {
			Date date = DateUtils.fomatDateTime(DateUtils.getAddDayHours(expirationHour))  ; //  new Date(System.currentTimeMillis() + time );
			Algorithm algorithm = Algorithm.HMAC256(salt);
			// 附带username信息
			return JWT.create().withClaim(ACCOUNT, username).withExpiresAt(date).withIssuedAt(new Date())
					.sign(algorithm);
		} catch (UnsupportedEncodingException e) {
			return null;
		}
	}

	/**
	 * token是否过期
	 * 
	 * @return true：过期
	 */
	public  boolean isTokenExpired(String token) {
		Date now = Calendar.getInstance().getTime();
		DecodedJWT jwt = JWT.decode(token);
		return jwt.getExpiresAt().before(now);
	}

	/**
	 * 生成随机盐,长度32位
	 * 
	 * @return
	 */
	public  String generateSalt() {
		SecureRandomNumberGenerator secureRandom = new SecureRandomNumberGenerator();
		String hex = secureRandom.nextBytes(16).toHex();
		return hex;
	}


	/**
	 * 获得token中的信息无需secret解密也能获得
	 *
	 * @return token中包含的用户名
	 */
	public  User getRedisUser(String token) {
		 String account = getAccount(token);
		 if(lumlordCacheServer.hasCache(account,User.class)){
		 	return (User) lumlordCacheServer.cache(account,User.class);
		 }else{
			 return null;
		 }
	}


	/**
	 * 获得token中的信息无需secret解密也能获得
	 *
	 * @return token中包含的用户名
	 */
	public  void setRedisUser(String token,User user) {
		String account = getAccount(token);
		Date timeOut = getExpiresAt(token);
		int lessTime = (int) (timeOut.getTime() - Calendar.getInstance().getTimeInMillis() ); //剩余时间
		lumlordCacheServer.cache(account,User.class,user,lessTime);
	}

	public void removeCacheUser(String token){
		String account = getAccount(token);
		lumlordCacheServer.delCache(account,User.class);
		lumlordCacheServer.delCache(account+loginInfoCache,User.class);
	}

	// cache begin
	//登录信息记录
	private final String loginInfoCache = ":loginInfo";
	//失败登录次数
	private final String loginFailTimes = loginInfoCache+":failTimes";
	//允许登录次数标记
	private final String allowLoginTimes = loginFailTimes+":allowLoginTimes";
	//账号登录 失败 锁定时间
	private final String lockTimes = loginFailTimes+":lockTimes";

	// cache end

	@Value("${token.expirationSeconds}")
	private  int expirationHour ;
	@Value("${token.salt}")
	private  String salt ;
	//单位时间内容
	@Value("${lumlord.security.unitMinute:5}")
	private Integer unitMinute;

	//运行失败次数
	@Value("${lumlord.security.allowFailLogin:5}")
	private  Integer allowFailLogin;

	//锁定等待时间
	@Value("${lumlord.security.lockWatitTime:5}")
	private Integer lockWatitTime;
	//单位时间内容
	@Value("${lumlord.security.unitMinuteLogin:1}")
	private Integer unitMinuteLogin;

	//运行失败次数
	@Value("${lumlord.security.allowSuccessLogin:10}")
	private  Integer allowSuccessLogin;

	@Autowired
	private  RedisUtil redisUtil;
	@Autowired
	private AccessAddressUtil accessAddressUtil;
	@Autowired
	private LumlordCacheServer lumlordCacheServer;
	@Autowired
	private RedisTemplate redisTemplate;
	@Autowired
	private SystemUserService systemUserService;

	//对应 user 的字段
	public static final String ACCOUNT = "account";
}
